<template>
  <div class="cart">
    <h4>全部商品</h4>
    <div class="cart-main">
      <div class="cart-th">
        <div class="cart-th1">全部</div>
        <div class="cart-th2">商品</div>
        <div class="cart-th3">单价（元）</div>
        <div class="cart-th4">数量</div>
        <div class="cart-th5">小计（元）</div>
        <div class="cart-th6">操作</div>
      </div>
      <div class="cart-body">
        <ul class="cart-list" v-for="shopCart in shopCartList" :key="shopCart.id">
          <li class="cart-list-con1">
            <input type="checkbox" name="chk_list" :checked="shopCart.isChecked" @change="changeStatus(shopCart)" />
          </li>
          <li class="cart-list-con2">
            <img :src="shopCart.imgUrl" />
            <div class="item-msg">{{ shopCart.skuName }}</div>
          </li>
          <li class="cart-list-con4">
            <span class="price">{{ shopCart.skuPrice }}.00</span>
          </li>
          <li class="cart-list-con5">
            <a href="javascript:void(0)" class="mins" @click="changeNum('decrement', shopCart)">-</a>
            <input autocomplete="off" type="text" minnum="1" class="itxt" @change="changeNum('changeNum', shopCart, $event)" :value="shopCart.skuNum" />
            <a href="javascript:void(0)" class="plus" @click="changeNum('increment', shopCart)">+</a>
            <!-- 在给这个添加删除修改数量的三个按钮中,得到这三个都是触发统一事件,那么如何判断具体是哪一个按钮触发的事件呢,我们不妨添加一个标识,例如按钮减'decrement',然后在下面的事件中,通过switch case来匹配对应的事件, 
            通过接口文档知道,我们需要传入差值来通过服务器修改数据,而且还需要参数知道点击的是哪一个商品 -->
          </li>
          <li class="cart-list-con6">
            <span class="sum">{{ shopCart.skuPrice * shopCart.skuNum }}</span>
          </li>
          <li class="cart-list-con7">
            <a href="JavaScript:;" @click="removeshop(shopCart.skuId)">删除</a>
            <br />
            <a href="#none">移到收藏</a>
          </li>
        </ul>
      </div>
    </div>
    <!-- 在这里早不到val,所以我们使用一个已知的变量代替,$event -->
    <!-- <Dialog v-show="isshow" :show="isshow" @props="isshow = $event"> -->
    <!-- <Dialog v-show="isshow" @props:show="isshow = $event"> -->
    <Dialog :show.sync="isshow">
      <template v-slot:header>
        <p>提示</p>
      </template>
      <template>
        <p>您真的确定要删除商品吗？</p>
      </template>
      <template #footer>
        <div class="btn-footer">
          <button class="btn btn-cancle" @click="cancel">取消</button>
          <button class="btn btn-primary" @click="delOneOrCheckedShop">确定</button>
        </div>
      </template>
    </Dialog>

    <div class="cart-tool">
      <div class="select-all">
        <input class="chooseAll" type="checkbox" v-model="checkAll" />
        <span>全选</span>
      </div>
      <div class="option">
        <a href="JavaScript:;" @click="delAllShopData">删除选中的商品</a>
        <a href="#none">移到我的关注</a>
        <a href="#none">清除下柜商品</a>
      </div>
      <div class="money-box">
        <div class="chosed">
          已选择 <span>{{ whatShop }}</span
          >件商品
        </div>
        <div class="sumprice">
          <em>总价（不含运费） ：</em>
          <i class="summoney">{{ whatPrice }}</i>
        </div>
        <div class="sumbtn">
          <a href="JavaScript:;" class="sum-btn" @click="isToken">结算</a>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { reqGetShopCarList, reqChangeStatusList, reqChangeAllCartState, reqOneshopData, reqAnyShopData, reqAddCar } from "@/api"
import Dialog from "@/components/Dialog"
import { skuNumReg } from "@/untils/reg"

// 由于现在没有登录,服务器要通过一个标识,来识别那个用户添加数据到购物车
// 如果用户登录,服务器会生成一token来当成身份标识
// 所以我们要使用到uuid,生成一个唯一标识符,来跟随请求体发送出去. 这个是由浏览器生成的
// 存在本地localstorage

// 由于数据只在本组件使用,所以不需要将数据存入vuex中

export default {
  name: "ShopCart",
  conponent: {
    Dialog,
  },
  data() {
    return {
      shopCartList: [],
      isshow: false,
      skuid: "",
      // 因为只有一个确认删除的按钮,所以需要判断是删除一条还是批量,所以存入一个标识符,判断
      isDelOne: true,
      DelisCheckedList: [],
      sum: 0,
      timeId: 0,
    }
  },
  watch: {
    isshow: {
      immediate: true, //上来就监视页面,如果有slot就禁用滚动条
      handler() {
        if (this.isshow) {
          document.documentElement.style.overflow = "hidden"
        } else {
          document.documentElement.style.overflow = "auto"
        }
      },
    },
  },
  computed: {
    // 复习一下,计算属性的好处,当依赖的数据发生变化是时,这个被计算的属性值也会随着变化
    // 计算属性也有两种写法,一种只是被动的计算数值,一种是修改自身来改变其他的数据 ，， 一个普通函数的写法，一个get和set写法
    // 计算属性和侦听属性的区别，一个有return， 侦听属性可使用nextTick（）来实现同步执行的操作

    checkAll: {
      get() {
        return this.shopCartList.every(item => item.isChecked === 1)
      },
      async set(newVal) {
        // 这里只能用三元运算符
        // 问题,此处三元的目的是为了让ischecked转换为布尔值,我们只需要一个简单的运算符,不改变结果即可,所以之前的思路错误,导致结果不对

        // let isChecked = newVal ? 1 : 0
        let isChecked = newVal - 0
        console.log(newVal, isChecked)
        const newList = []
        this.shopCartList.forEach(item => {
          item.isChecked = isChecked
          newList.push(item.skuId)
        })
        const result = await reqChangeAllCartState(isChecked, newList)
        if (result.code === 200) {
          // alert("更新成功...")
        } else {
          console.log("更新失败...")
        }
      },
    },
    whatShop() {
      // 勾选商品总数
      return this.shopCartList.filter(item => item.isChecked == 1).length
    },
    whatPrice() {
      // 商品总价 ==  选中的商品->单价乘以数量 我们需要判断是否选中,刚好isChecked值为1或0,我们可以将isChecked乘以价格,如果选中就是正常,没选中就是0
      // 问题: 一定要主要是否需要加括号,加了括号一定要使用return
      return this.shopCartList.reduce((prev, curr) => prev + curr.isChecked * curr.skuNum * curr.skuPrice, 0)
    },
  },
  methods: {
    // 由于需要每次操作数据就要重新向服务器发送请求,所以将发送请求的函数封装
    async sendReq() {
      const result = await reqGetShopCarList()
      if (result.code == 200) {
        // 问题 当购物车中没有一条数据时,控制台会报错,原因是所有方法都是基于vuex中的data数据,一旦data没有数据,依赖data的方法都会报错,我们可以在这里进行判断,有数据就添加,没数据就给个空数组,避免报错
        this.shopCartList = result.data[0] ? result.data[0].cartInfoList : []
      }
    },

    // 修改一项任务状态
    async changeStatus(shopCart) {
      // 因为ischecked是number值,1和0,我们可以巧妙的利用
      // 如
      // 首先获取保存一个修改后的ischecked的值
      let isChecked = 1 - shopCart.isChecked
      // 然后将修改后的ischecked赋值给那个点击的商品
      // 重新发送请求获取页面数据
      const result = await reqChangeStatusList(shopCart.skuId, isChecked)
      // 其实响应式框架,只要修改服务器的数据,当页面重新加载时,会自动更新数据
      // 问题::: 其实当点击修改后,我们只是修改了服务器的状态和,当前的页面的样式,但是当我们监听全选按钮时,无法正常的侦听
      // 因为我们并没有将本组件依赖的数据进行修改, 所以页面只是表面变化, isChecked还是没有改变
      // 只有当我们重新刷新页面时,重新获取服务器的数据,然后才会将ischecked的值改变
      // 具体可看vuex中的ischecked的变化

      if (result.code === 200) {
        // 让页面中的状态发生改变一下即可
        shopCart.isChecked = isChecked
      } else {
        console.log(result.message)
      }
    },

    // 移除一样商品
    // 点击取消,不删除商品,并隐藏slot
    cancel() {
      this.isshow = false
      // this.$bus.$on("showed")
    },

    // 点击删除获取id
    removeshop(id) {
      // 让插槽显示
      this.isshow = true
      // 将获取到的id赋值给组件
      this.skuid = id
    },

    // 点击批量删除,用一个数组将选中的id存好,后面点击确认时发送请求
    delAllShopData() {
      // 获取选中的商品的id,并存入组件内的数组中
      this.shopCartList.forEach(item => {
        if (item.isChecked == 1) {
          this.DelisCheckedList.push(item.skuId)
        }
      })
      // 展示提示框
      this.isshow = true
      // 改变标识符,让提示框知道这次是批量删除
      this.isDelOne = false
    },

    // 真正删除时,根据获取的id发送请求
    async delOneOrCheckedShop() {
      // 删除一条
      if (this.isDelOne) {
        await reqOneshopData(this.skuid)
        //删除完了将提示去掉
        this.isshow = false
        //重新获取数据
      }
      if (!this.isDelOne) {
        // 批量删除,由于接口问题,批量删除暂时无法验证
        reqAnyShopData(this.DelisCheckedList).then(
          () => {
            alert("删除成功...")
          },
          () => {
            // 还有一个问题,当删除完后,需要将数组清空,不然下次发送请求,会将之前的skuid一起发送过去,不好
            this.DelisCheckedList = []
            // 这里有一个注意点,由于ajax请求是异步的,返回值也是,所以一旦加了async和await,当服务器响应失败时,后面的代码都不会执行了,我们也可以利用这个特性来完成一些事情

            alert("批量删除失败...")
            //  //删除完了将提示去掉
            this.isshow = false
          }
        )
      }
      this.sendReq()
    },

    // 修改商品数量
    changeNum(type, shopCartList, e) {
      // 使用一个中间变量来发送请求
      let { skuNum, skuId } = shopCartList
      // 这里还是需要一个对象传过来, 因为我们需要操作页面中的input值
      // let sum = 0
      // 存一个原值
      let oldNum = shopCartList.skuNum
      switch (type) {
        case "decrement":
          // 按钮减
          shopCartList.skuNum--
          this.sum--
          // 一直点击会变成负数
          if (shopCartList.skuNum < 1) {
            // 加一个判断,如果小于1,那就等于1
            shopCartList.skuNum = 1
            this.sum = 0
          }
          break
        case "increment":
          // 按钮加
          shopCartList.skuNum++
          this.sum++
          if (shopCartList.skuNum > 200) {
            // 加一个判断,如果大于200,那就等于200
            shopCartList.skuNum = 200
            this.sum = 0
          }
          break
        // #region
        // case "changeNum":
        //   // 在这个事件中,我们不能用点击事件,需要使用change事件,因为我们得让用户输入完后获取新的值
        //   // 首先需要获取用户输入的值,我们这次需要一个已知变量来保存,比如$event
        //   // 其中还有一些特殊情况
        //   // 比如输入的值不合法,输入的值超出库存等
        //   // 我们可以进行一个简单的判断,  利用强大的正则
        //   if (skuNumReg.test(e.target.value)) {
        //     this.sum = e.target.value - shopCartList.skuNum // 差值
        //     // console.log(sum)
        //     shopCartList.skuNum = e.target.value
        //     break
        //   } else {
        //     this.sum = 0
        //     e.target.value = 1
        //     break
        //   }
        // #endregion
        case "changeNum":
          // 直接修改数据
          let newNum = e.target.value // 用户新输入的数据
          // let oldNum = cartInfo.skuNum
          if (skuNumReg.test(newNum)) {
            // 页面中显示出来的值
            shopCartList.skuNum = newNum
            // 发送给服务器的差值
            // num = newNum - oldNum
            this.num = newNum - oldNum
            if (newNum > 200) {
              // console.log("大于200了...")
              shopCartList.skuNum = 200
              // num = 200 - oldNum
              this.num = 200 - oldNum
            }
          } else {
            // 如果输入用户的是非法值 还是让结果为原值
            // cartInfo.skuNum = oldNum
            e.target.value = oldNum
          }
          break
      }

      // 由于不需要发送那么多次请求,真加服务器压力,我们可以在这里进行防抖操作
      // 注意:在switch中一定不使用return,否则会直接退出函数,只能用break退出当前分支.继续执行下面的其他操作
      if (this.sum == 0) return
      if (this.timeId) clearTimeout(this.timeId)
      // 将发送请求的函数用一个定时器包裹,延迟0.5s发送请求,如果在0.5s内再次发送,则在过0.5s后发送.达到防抖效果
      this.timeId = setTimeout(() => {
        reqAddCar(skuId, this.sum)
      }, 500)
    },

    // 判断是否携带token
    isToken() {
      if (localStorage.getItem("token")) {
        this.$router.push("/Trade")
      } else {
        this.$router.push("/Login")
      }
    },
  },
  mounted() {
    // 在页面挂载完毕就获取数据渲染
    this.sendReq()
  },
}
</script>

<style lang="less" scoped>
.cart {
  width: 1200px;
  margin: 0 auto;
  h4 {
    margin: 9px 0;
    font-size: 14px;
    line-height: 21px;
  }
  .cart-main {
    .cart-th {
      background: #f5f5f5;
      border: 1px solid #ddd;
      padding: 10px;
      overflow: hidden;
      & > div {
        float: left;
      }
      .cart-th1 {
        width: 25%;
        input {
          vertical-align: middle;
        }
        span {
          vertical-align: middle;
        }
      }
      .cart-th2 {
        width: 25%;
      }
      .cart-th3,
      .cart-th4,
      .cart-th5,
      .cart-th6 {
        width: 12.5%;
      }
    }
    .cart-body {
      margin: 15px 0;
      border: 1px solid #ddd;
      .cart-list {
        padding: 10px;
        border-bottom: 1px solid #ddd;
        overflow: hidden;
        & > li {
          float: left;
        }
        .cart-list-con1 {
          width: 15%;
        }
        .cart-list-con2 {
          width: 35%;
          img {
            width: 82px;
            height: 82px;
            float: left;
          }
          .item-msg {
            float: left;
            width: 150px;
            margin: 0 10px;
            line-height: 18px;
          }
        }
        .cart-list-con4 {
          width: 10%;
        }
        .cart-list-con5 {
          width: 17%;
          .mins {
            border: 1px solid #ddd;
            border-right: 0;
            float: left;
            color: #666;
            width: 6px;
            text-align: center;
            padding: 8px;
          }
          input {
            border: 1px solid #ddd;
            width: 40px;
            height: 33px;
            float: left;
            text-align: center;
            font-size: 14px;
          }

          .plus {
            border: 1px solid #ddd;
            border-left: 0;
            float: left;
            color: #666;
            width: 6px;
            text-align: center;
            padding: 8px;
          }
        }

        .cart-list-con6 {
          width: 10%;

          .sum {
            font-size: 16px;
          }
        }

        .cart-list-con7 {
          width: 13%;

          a {
            color: #666;
          }
        }
      }
    }
  }
  .cart-tool {
    overflow: hidden;
    border: 1px solid #ddd;

    .select-all {
      padding: 10px;
      overflow: hidden;
      float: left;

      span {
        vertical-align: middle;
      }

      input {
        vertical-align: middle;
      }
    }

    .option {
      padding: 10px;
      overflow: hidden;
      float: left;

      a {
        float: left;
        padding: 0 10px;
        color: #666;
      }
    }

    .money-box {
      float: right;

      .chosed {
        line-height: 26px;
        float: left;
        padding: 0 10px;
      }

      .sumprice {
        width: 200px;
        line-height: 22px;
        float: left;
        padding: 0 10px;

        .summoney {
          color: #c81623;
          font-size: 16px;
        }
      }

      .sumbtn {
        float: right;

        a {
          display: block;
          position: relative;
          width: 96px;
          height: 52px;
          line-height: 52px;
          color: #fff;
          text-align: center;
          font-size: 18px;
          font-family: "Microsoft YaHei";
          background: #e1251b;
          overflow: hidden;
        }
      }
    }
  }
}
</style>
